home *** CD-ROM | disk | FTP | other *** search
/ BCI NET 2 / BCI NET 2.iso / archives / telecomm / misc / xprzmodem.lha / Source / send.c < prev    next >
Encoding:
C/C++ Source or Header  |  1995-02-18  |  18.0 KB  |  684 lines

  1. /**********************************************************************
  2.  * Send.c: File transmission routines for xprzmodem.library;
  3.  * Original Version 2.10, 12 February 1991, by Rick Huebner.
  4.  * Based closely on Chuck Forsberg's sz.c example ZModem code,
  5.  * but too pervasively modified to even think of detailing the changes.
  6.  * Released to the Public Domain; do as you like with this code.
  7.  *
  8.  * Version 2.50, 15 November 1991, CRC-32 additions by William M. Perkins.
  9.  * Version 2.51 29, January 1992, RX_timout fix by John Tillema
  10.  * Version 2.52   6 March 1992, Very minor fix with compiled 020 library
  11.  *               by William M. Perkins.
  12.  * Version 2.53, 28 June 1993, several additions by Olaf `Olsen' Barthel
  13.  **********************************************************************/
  14.  
  15. /* Convert decimal to octal value. */
  16. STATIC VOID __regargs
  17. to_octal(STRPTR buf, ULONG value)
  18. {
  19.   UBYTE buffer[20];
  20.   register short i = 1;
  21.  
  22.   while (value)
  23.   {
  24.     buffer[i++] = '0' + (value & 7);
  25.  
  26.     value >>= 3;
  27.   }
  28.  
  29.   do
  30.     *buf++ = buffer[--i];
  31.   while (i);
  32.  
  33.   *buf = 0;
  34. }
  35.  
  36. /**********************************************************
  37.  *      long XProtocolSend(struct XPR_IO *xio)
  38.  *
  39.  * Main file transmission routine; called by comm program
  40.  **********************************************************/
  41. long __saveds __asm XProtocolSend(register __a0 struct XPR_IO *xio)
  42. {
  43.   struct Vars *v;
  44.   short err;
  45.  
  46.   /* Perform common setup and initializations */
  47.   if (!(v = setup(xio)))
  48.     return XPRS_FAILURE;
  49.  
  50. /*  was 600, set to 300 to fix so it uploads correctly */
  51.   v->Rxtimeout = 300;
  52.   v->Wantfcs32 = TRUE;
  53.   v->Rxflags = 0;
  54.   v->Receiving = FALSE;
  55.  
  56.   /* Transfer the files */
  57.   zmputs(v, "rz\r");
  58.   stohdr(v, 0L);
  59.   zshhdr(v, ZRQINIT);
  60.   sendbuf(v);
  61.   if (getzrxinit(v) == ERROR)
  62.     upderr(v, GetLocaleString(MSG_UPLOAD_CANCELED_TXT));
  63.   else
  64.     sendbatch(v);
  65.  
  66.   /* Clean up and return */
  67.   if (err = v->Errcnt)
  68.     upderr(v, GetLocaleString(MSG_FILES_SKIPPED_DUE_TO_ERRORS_TXT));
  69.   else
  70.     updmsg(v, GetLocaleString(MSG_DONE_TXT));
  71.   if (v->io.xpr_setserial && v->Oldstatus != -1)
  72.     (*v->io.xpr_setserial) (v->Oldstatus);
  73.   FreeMem(v->Filebuf, v->Filebufmax);
  74.   FreeMem(v, (long) sizeof(struct Vars));
  75.  
  76.   return (err) ? XPRS_FAILURE : XPRS_SUCCESS;
  77. }                /* End of long XProtocolSend() */
  78.  
  79. /**********************************************************
  80.  *      short getzrxinit(struct Vars *v)
  81.  *
  82.  * Negotiate with receiver to start a file transfer
  83.  **********************************************************/
  84. short getzrxinit(struct Vars *v)
  85. {
  86.   short n;
  87.  
  88.   for (n = v->ErrorLimit; --n >= 0;)
  89.   {
  90.     /* Check for abort from comm program */
  91.     if (v->io.xpr_chkabort && (*v->io.xpr_chkabort) () < 0)
  92.     {
  93.       canit(v); /* Receiver does not respond to ZABORT. */
  94.       return ERROR;
  95.     }
  96.     switch (zgethdr(v))
  97.     {
  98.     case ZCHALLENGE:        /* Echo receiver's challenge number */
  99.       stohdr(v, v->Rxpos);
  100.       zshhdr(v, ZACK);
  101.       sendbuf(v);
  102.       continue;
  103.     case ZCOMMAND:        /* They didn't see our ZRQINIT; try again */
  104.       stohdr(v, 0L);
  105.       zshhdr(v, ZRQINIT);
  106.       sendbuf(v);
  107.       continue;
  108.     case ZRINIT:        /* Receiver ready; get transfer parameters */
  109.       v->Rxflags = 0xFF & v->Rxhdr[ZF0];
  110.       v->Txfcs32 = (v->Wantfcs32 && (v->Rxflags & CANFC32));
  111.       v->Zctlesc |= v->Rxflags & TESCCTL;
  112.       v->Rxbuflen = ((USHORT) v->Rxhdr[ZP1] << 8) | v->Rxhdr[ZP0];
  113.       /* Use shortest of the two side's max frame lengths */
  114.       if (v->Tframlen && (!v->Rxbuflen || v->Tframlen < v->Rxbuflen))
  115.     v->Rxbuflen = v->Tframlen;
  116.       return (sendzsinit(v));
  117.     case ZCAN:
  118.     case RCDO:
  119.     case TIMEOUT:
  120.       upderr(v, v->Msgbuf);
  121.       return ERROR;
  122.     case ZRQINIT:
  123.       if (v->Rxhdr[ZF0] == ZCOMMAND)
  124.     continue;
  125.       /* fallthrough... */
  126.     default:
  127.       zshhdr(v, ZNAK);
  128.       sendbuf(v);
  129.       continue;
  130.     }
  131.   }
  132.   return ERROR;
  133. }                /* End of short getzrxinit() */
  134.  
  135. /* Send send-init information */
  136. short sendzsinit(struct Vars *v)
  137. {
  138.   if (v->Attn[0] == '\0' && (!v->Zctlesc || (v->Rxflags & TESCCTL)))
  139.     return OK;
  140.   else
  141.   {
  142.     register short c, errors = 0;
  143.  
  144.     for (;;)
  145.     {
  146.       stohdr(v, 0L);
  147.       if (v->Zctlesc)
  148.       {
  149.     v->Txhdr[ZF0] |= TESCCTL;
  150.     zshhdr(v, ZSINIT);
  151.       }
  152.       else
  153.     zsbhdr(v, ZSINIT);
  154.  
  155.       v->Outbuf[0] = 0;
  156.       zsdata(v, 1, ZCRCW);
  157.  
  158.       c = zgethdr(v);
  159.       switch (c)
  160.       {
  161.       case ZCAN:
  162.     return ERROR;
  163.       case ZACK:
  164.     return OK;
  165.       default:
  166.     if (++errors > v->ErrorLimit)
  167.       return ERROR;
  168.     continue;
  169.       }
  170.     }
  171.   }
  172. }                /* End of short sendzsinit() */
  173.  
  174. /**********************************************************
  175.  *      void sendbatch(struct Vars *v)
  176.  *
  177.  * Send a batch of files
  178.  **********************************************************/
  179. void sendbatch(struct Vars *v)
  180. {
  181.   UBYTE single, done = FALSE;
  182.   long fstate;
  183.  
  184.   /* If template routines not provided, must be single filename */
  185.   if (!v->io.xpr_ffirst || !v->io.xpr_fnext)
  186.   {
  187.     single = TRUE;
  188.     strcpy(v->Filename, v->io.xpr_filename);
  189.     /* Else use the template routines to get the first filename */
  190.   }
  191.   else
  192.   {
  193.     single = FALSE;
  194.     fstate = (*v->io.xpr_ffirst) (v->Filename, v->io.xpr_filename);
  195.     if (!fstate)
  196.     {
  197.       upderr(v, GetLocaleString(MSG_NO_FILES_MATCH_TEMPLATE_TXT));
  198.       return;
  199.     }
  200.   }
  201.  
  202.   /* If using templates, keep getting names & sending until done */
  203.   while (!done)
  204.   {
  205.     if (sendone(v) == ERROR)
  206.       return;
  207.     if (single)
  208.       break;
  209.     fstate = (*v->io.xpr_fnext) (fstate, v->Filename, v->io.xpr_filename);
  210.     done = !fstate;
  211.   }
  212.  
  213.   /* End batch and return; if we never got started, just cancel receiver */
  214.   if (v->Filcnt)
  215.     saybibi(v);
  216.   else
  217.     canit(v);
  218. }                /* End of void sendbatch() */
  219.  
  220. /**********************************************************
  221.  *      short sendone(struct Vars *v)
  222.  *
  223.  * Send the file named in v->Filename
  224.  **********************************************************/
  225. short sendone(struct Vars *v)
  226. {
  227.   struct SetupVars *sv;
  228.  
  229.   /* Display name of file being sent for user */
  230.   v->xpru.xpru_updatemask = XPRU_FILENAME;
  231.   v->xpru.xpru_filename = v->Filename;
  232.   (*v->io.xpr_update) (&v->xpru);
  233.  
  234.   /* Set text/binary mode according to options before opening file */
  235.   set_textmode(v);
  236.  
  237.   /* Open the file, if possible */
  238.   if (!(v->File = bfopen(v, "r")))
  239.   {
  240.     ++v->Errcnt;
  241.     upderr(v, GetLocaleString(MSG_CANT_OPEN_FILE_TXT));
  242.     return OK;            /* pass over it, there may be others */
  243.   }
  244.   ++v->Filcnt;
  245.   GetSysTime(&v->Starttime);
  246.   v->BytesSent = 0;
  247.  
  248.   /* Kick off the file transfer */
  249.   sv = (void *) v->io.xpr_data;
  250.   switch (sendname(v))
  251.   {
  252.   case ERROR:
  253.     ++v->Errcnt;
  254.     return ERROR;
  255.   case OK:
  256.     bfclose(v);
  257.     /* File sent; if option DY, delete file after sending */
  258.     if (*sv->option_d == 'Y' && v->io.xpr_extension >= 2 && v->io.xpr_unlink)
  259.     {
  260.       updmsg(v, GetLocaleString(MSG_DELETING_FILE_AFTER_SEND_TXT));
  261.       (*v->io.xpr_unlink) (v->Filename);
  262.     }
  263.     break;
  264.   }
  265.   return OK;
  266. }                /* End of short sendone() */
  267.  
  268. /**********************************************************
  269.  *      short sendname(struct Vars *v)
  270.  *
  271.  * Build file info block consisting of file name, length,
  272.  * time, and mode
  273.  **********************************************************/
  274. short sendname(struct Vars *v)
  275. {
  276.   struct SetupVars *sv;
  277.   UBYTE *p, *q, buff[32], mode[5];
  278.   BPTR FileLock;
  279.  
  280.   /* Initialize comm program transfer status display */
  281.   v->Fsize = (v->io.xpr_finfo) ? (*v->io.xpr_finfo) (v->Filename, 1L) : -1;
  282.   v->xpru.xpru_updatemask = XPRU_PROTOCOL | XPRU_FILESIZE | XPRU_MSG
  283.     | XPRU_BLOCKS | XPRU_ERRORS | XPRU_TIMEOUTS | XPRU_BLOCKCHECK
  284.     | XPRU_BYTES | XPRU_ELAPSEDTIME;
  285.   v->xpru.xpru_protocol = "ZModem";
  286.   v->xpru.xpru_filesize = v->Fsize;
  287.   v->xpru.xpru_msg = (v->Lzconv == ZCNL) ? GetLocaleString(MSG_SENDING_TEXT_FILE_TXT) :
  288.     ((v->Lzconv == ZCBIN) ? GetLocaleString(MSG_SENDING_BINARY_FILE_TXT) : GetLocaleString(MSG_SENDING_FILE_TXT));
  289.   v->xpru.xpru_blocks = v->xpru.xpru_errors = v->xpru.xpru_timeouts = 0;
  290.   v->xpru.xpru_blockcheck = v->Crc32t ? "CRC-32" : "CRC-16";
  291.   v->xpru.xpru_bytes = v->Strtpos = 0;
  292.   v->xpru.xpru_blocksize = v->ksize;
  293.   update_rate(v);
  294.   (*v->io.xpr_update) (&v->xpru);
  295.  
  296.   sv = (void *) v->io.xpr_data;
  297.   if (*sv->option_s == 'Y')
  298.   {
  299.     /* If "SY" option selected, send full path */
  300.     strcpy(v->Pktbuf, v->Filename);
  301.     p = v->Pktbuf + strlen(v->Pktbuf) + 1;
  302.   }
  303.   else
  304.   {
  305.     /* else extract outgoing file name without directory path */
  306.     for (p = v->Filename, q = v->Pktbuf; *p; ++p, ++q)
  307.       if ((*q = *p) == '/' || *q == ':')
  308.     q = v->Pktbuf - 1;
  309.     *q = '\0';
  310.     p = ++q;
  311.   }
  312.  
  313.   /* Zero out remainder of file info packet */
  314.   memset(p, 0, sizeof(v->Pktbuf) - (p - v->Pktbuf));
  315.  
  316.   /* Store file size, timestamp, and mode in info packet */
  317.   if (v->FileAttributes & 1)
  318.   {
  319.     ULONG Seconds = getsystime(NULL), Mode = 0000;    /* owner = rwx, group = rwx, others = rwx */
  320.  
  321.     /* See if we can lock it, this probably won't work for `term'. */
  322.     if (FileLock = Lock(v->Filename, ACCESS_READ))
  323.     {
  324.       struct FileInfoBlock __aligned FileInfo;
  325.  
  326.       /* Any info available? */
  327.       if (Examine(FileLock, &FileInfo))
  328.       {
  329.         /* Modification date. */
  330.     Seconds = (FileInfo.fib_Date.ds_Days * 24 * 60 + FileInfo.fib_Date.ds_Minute) * 60 + (FileInfo.fib_Date.ds_Tick) / TICKS_PER_SECOND + GMT_Offset;
  331.  
  332.         /* Take care of the owner bits. */
  333.     if (FileInfo.fib_Protection & FIBF_EXECUTE)
  334.       Mode |= 0100;
  335.  
  336.     if (FileInfo.fib_Protection & (FIBF_WRITE | FIBF_DELETE))
  337.       Mode |= 0200;
  338.  
  339.     if (FileInfo.fib_Protection & FIBF_READ)
  340.       Mode |= 0400;
  341.  
  342.         /* Take care of the group bits. */
  343.     if (!(FileInfo.fib_Protection & FIBF_GRP_EXECUTE))
  344.       Mode |= 0010;
  345.  
  346.     if (!(FileInfo.fib_Protection & FIBF_GRP_WRITE) || !(FileInfo.fib_Protection & FIBF_GRP_DELETE))
  347.       Mode |= 0020;
  348.  
  349.     if (!(FileInfo.fib_Protection & FIBF_GRP_READ))
  350.       Mode |= 0040;
  351.  
  352.         /* Take care of the other bits. */
  353.     if (!(FileInfo.fib_Protection & FIBF_OTR_EXECUTE))
  354.       Mode |= 0001;
  355.  
  356.     if (!(FileInfo.fib_Protection & FIBF_OTR_WRITE) || !(FileInfo.fib_Protection & FIBF_OTR_DELETE))
  357.       Mode |= 0002;
  358.  
  359.     if (!(FileInfo.fib_Protection & FIBF_OTR_READ))
  360.       Mode |= 0004;
  361.       }
  362.  
  363.       UnLock(FileLock);
  364.     }
  365.  
  366.     to_octal(buff, Seconds);
  367.     to_octal(mode, Mode);
  368.   }
  369.   else
  370.   {
  371.     to_octal(buff, getsystime(NULL));
  372.     to_octal(mode, 0000);
  373.   }
  374.  
  375.   /* amiga.lib mysprintf() can't do %lo format, so we do it the hard way */
  376.   /* Yes, octal; ZModem was originally done on Unix, and they like octal there */
  377.  
  378.   mysprintf(p, "%ld %s %s", (v->Fsize < 0) ? 0L : v->Fsize, buff, mode);
  379.  
  380.   /* Send filename packet */
  381.   return zsendfile(v, (short) (p - v->Pktbuf + strlen(p) + 1));
  382. }                /* End of short sendname() */
  383.  
  384. /**********************************************************
  385.  *      short zsendfile(struct Vars *v, short blen)
  386.  *
  387.  * Send the filename packet and see if receiver will accept
  388.  * file
  389.  **********************************************************/
  390. short zsendfile(struct Vars *v, short blen)
  391. {
  392.   short c;
  393.  
  394.   while (TRUE)
  395.   {
  396.     v->Txhdr[ZF0] = v->Lzconv;    /* Text or Binary mode; from config string */
  397.     v->Txhdr[ZF1] = LZMANAG;    /* Default file management mode */
  398.     v->Txhdr[ZF2] = LZTRANS;    /* Default file transport mode */
  399.     v->Txhdr[ZF3] = 0;
  400.     zsbhdr(v, ZFILE);
  401.     zsdata(v, blen, ZCRCW);
  402.     sendbuf(v);
  403.   again:
  404.     /* Check for abort from comm program */
  405.     if (v->io.xpr_chkabort && (*v->io.xpr_chkabort) () < 0)
  406.     {
  407.       bfclose(v);
  408.       canit(v); /* Receiver does not respond to ZABORT. */
  409.       return ERROR;
  410.     }
  411.     switch (c = zgethdr(v))
  412.     {
  413.     case ZRINIT:
  414.       goto again;
  415.     case ZCAN:
  416.     case ZCRC:
  417.     case RCDO:
  418.     case TIMEOUT:
  419.     case ZABORT:
  420.     case ZFIN:
  421.       upderr(v, v->Msgbuf);
  422.       return ERROR;
  423.     case ZSKIP:        /* Receiver doesn't want this one */
  424.       upderr(v, GetLocaleString(MSG_SKIP_COMMAND_RECEIVED_TXT));
  425.       bfclose(v);
  426.       return c;
  427.     case ZRPOS:        /* Receiver wants it; this is starting position */
  428.       bfseek(v, v->Rxpos);
  429.       v->Strtpos = v->Txpos = v->Rxpos;
  430.       if (v->io.xpr_sflush)
  431.     (*v->io.xpr_sflush) ();
  432.       v->Modemcount = 0;
  433.       return zsendfdata(v);
  434.     }
  435.   }
  436. }                /* End of short zsendfile() */
  437.  
  438. /**********************************************************
  439.  *      short zsendfdata(struct Vars *v)
  440.  *
  441.  * Send the file data
  442.  **********************************************************/
  443. short zsendfdata(struct Vars *v)
  444. {
  445.   short c, e, blklen, goodbytes = 0;
  446.   USHORT framelen, maxblklen, goodneeded = 512;
  447.  
  448.   /* Figure out max data packet size to send */
  449.   maxblklen = v->ksize;
  450.   if (v->Rxbuflen && maxblklen > v->Rxbuflen)
  451.     maxblklen = v->Rxbuflen;
  452.   blklen = (v->Baud < 1200) ? 256 : v->ksize;
  453.   if (blklen > maxblklen)
  454.     blklen = maxblklen;
  455.  
  456.   /* If an interruption happened, handle it; else keep sending data */
  457. somemore:
  458.   while (char_avail(v))
  459.   {
  460.     /* Check for another incoming packet while discarding line noise */
  461.     switch (readock(v, 1))
  462.     {
  463.     case CAN:
  464.     case RCDO:
  465.     case ZPAD:
  466.       break;
  467.     default:
  468.       continue;
  469.     }
  470.   waitack:
  471.     switch (c = getinsync(v))
  472.     {
  473.     default:
  474.       upderr(v, GetLocaleString(MSG_TRANSFER_CANCELED_TXT));
  475.       bfclose(v);
  476.       return ERROR;
  477.     case ZSKIP:        /* Receiver changed its mind and wants to skip the file */
  478.       bfclose(v);
  479.       return c;
  480.     case ZACK:            /* ACK at end of frame; resume sending data */
  481.       break;
  482.     case ZRPOS:        /* An error; resend data from last good point */
  483.       blklen >>= 2;
  484.       if (blklen < MINBLOCK)
  485.     blklen = MINBLOCK;
  486.       if (goodneeded < MAXGOODNEEDED)
  487.     goodneeded <<= 1;
  488.       v->xpru.xpru_updatemask = XPRU_ERRORS;
  489.       ++v->xpru.xpru_errors;
  490.       (*v->io.xpr_update) (&v->xpru);
  491.       break;
  492.     case ZRINIT:
  493.       updmsg(v, GetLocaleString(MSG_DONE_TXT));
  494.       return OK;
  495.     }
  496.   }
  497.  
  498.   /* Transmit ZDATA frame header */
  499.   framelen = v->Rxbuflen;
  500.   stohdr(v, v->Txpos);
  501.   zsbhdr(v, ZDATA);
  502.  
  503.   /* Keep sending data packets until finished or interrupted */
  504.   do
  505.   {
  506.     /* Read next chunk of file data */
  507.     c = bfread(v, v->Pktbuf, (long) blklen);
  508.  
  509.     /* Figure out how to handle this data packet */
  510.     if (c < blklen)
  511.       e = ZCRCE;        /* If end of file, this is last data packet */
  512.     else if (v->Rxbuflen && (framelen -= c) <= 0)
  513.       e = ZCRCW;        /* If end of frame, ask for ACK */
  514.     else
  515.       e = ZCRCG;        /* Else tell receiver to expect more data packets */
  516.  
  517.     zsdata(v, c, e);        /* Send the packet */
  518.     sendbuf(v);
  519.  
  520.     /* Update comm program status display */
  521.     v->xpru.xpru_updatemask = XPRU_BLOCKS | XPRU_BLOCKSIZE | XPRU_BYTES
  522.       | XPRU_ELAPSEDTIME | XPRU_BLOCKCHECK;
  523.     ++v->xpru.xpru_blocks;
  524.     v->xpru.xpru_blocksize = c;
  525.     v->xpru.xpru_blockcheck = v->Crc32t ? "CRC-32" : "CRC-16";
  526.     v->xpru.xpru_bytes = v->Txpos += c;
  527.     update_rate(v);
  528.     (*v->io.xpr_update) (&v->xpru);
  529.  
  530.     /* If we've been sending smaller than normal packets, see if it's
  531.      * time to bump the packet size up a notch yet
  532.      */
  533.     if (blklen < maxblklen && (goodbytes += c) >= goodneeded)
  534.     {
  535.       blklen <<= 1;
  536.       if (blklen > maxblklen)
  537.     blklen = maxblklen;
  538.       goodbytes = 0;
  539.     }
  540.  
  541.     /* Give comm program its timeslice if it needs one */
  542.     if (v->io.xpr_chkmisc)
  543.       (*v->io.xpr_chkmisc) ();
  544.     /* Check for abort from comm program */
  545.     if (v->io.xpr_chkabort)
  546.     {
  547.       long result = (*v->io.xpr_chkabort) ();
  548.  
  549.       if(result < 0)
  550.       {
  551.         canit(v); /* Receiver does not respond to ZABORT. */
  552.         goto aborted;
  553.       }
  554.     }
  555.     /* If this was last packet in frame, go wait for ACK from receiver */
  556.     if (e == ZCRCW)
  557.       goto waitack;
  558.  
  559.     /*
  560.        * Check if receiver trying to interrupt us; look for incoming packet
  561.        * while discarding line noise
  562.      */
  563.     while (char_avail(v))
  564.     {
  565.       switch (readock(v, 1))
  566.       {
  567.       case CAN:
  568.       case RCDO:
  569.       case ZPAD:
  570.     /* Interruption detected; stop sending and process complaint */
  571.     zsdata(v, 0, ZCRCE);
  572.     sendbuf(v);
  573.     goto waitack;
  574.       }
  575.     }
  576.   }
  577.   while (e == ZCRCG);        /* If no interruption, keep sending data packets */
  578.  
  579.   /* Done sending file data; send EOF and wait for receiver to acknowledge */
  580.   while (TRUE)
  581.   {
  582.     updmsg(v, GetLocaleString(MSG_SENDING_EOF_TXT));
  583.     stohdr(v, v->Txpos);
  584.     zsbhdr(v, ZEOF);
  585.     sendbuf(v);
  586.     switch (c = getinsync(v))
  587.     {
  588.     case ZACK:
  589.       continue;
  590.     case ZRPOS:
  591.       goto somemore;
  592.     case ZRINIT:
  593.       updmsg(v, GetLocaleString(MSG_EOF_ACKNOWLEDGED_TXT));
  594.       update_rate(v);
  595.       v->xpru.xpru_updatemask = XPRU_ELAPSEDTIME;
  596.       (*v->io.xpr_update) (&v->xpru);
  597.       return OK;
  598.     case ZSKIP:
  599.       bfclose(v);
  600.       return c;
  601.     default:
  602.     aborted:
  603.       upderr(v, GetLocaleString(MSG_TRANSFER_CANCELED_TXT));
  604.       bfclose(v);
  605.       return ERROR;
  606.     }
  607.   }
  608. }                /* End of short zsendfdata() */
  609.  
  610. /**********************************************************
  611.  *      short getinsync(struct Vars *v)
  612.  *
  613.  * Respond to receiver's complaint, get back in sync with
  614.  * receiver
  615.  **********************************************************/
  616. short getinsync(struct Vars *v)
  617. {
  618.   short c;
  619.  
  620.   while (TRUE)
  621.   {
  622.     c = zgethdr(v);
  623.     if (v->io.xpr_sflush)
  624.       (*v->io.xpr_sflush) ();
  625.     v->Modemcount = 0;
  626.     switch (c)
  627.     {
  628.     case ZCAN:
  629.     case ZABORT:
  630.     case ZFIN:
  631.     case RCDO:
  632.     case TIMEOUT:
  633.       upderr(v, v->Msgbuf);
  634.       return ERROR;
  635.     case ZRPOS:
  636.       bfseek(v, v->Rxpos);
  637.       v->Txpos = v->Rxpos;
  638.       mysprintf(v->Msgbuf, GetLocaleString(MSG_RESENDING_FROM_TXT), v->Txpos);
  639.       upderr(v, v->Msgbuf);
  640.       return c;
  641.     case ZSKIP:
  642.       upderr(v, GetLocaleString(MSG_SKIP_COMMAND_RECEIVED_TXT));
  643.       /* fallthrough... */
  644.     case ZRINIT:
  645.       bfclose(v);
  646.       /* fallthrough... */
  647.     case ZACK:
  648.       return c;
  649.     default:
  650.       zsbhdr(v, ZNAK);
  651.       sendbuf(v);
  652.       continue;
  653.     }
  654.   }
  655. }                /* End of short getinsync() */
  656.  
  657. /**********************************************************
  658.  *      void saybibi(struct Vars *v)
  659.  *
  660.  * End of batch transmission; disengage cleanly from receiver
  661.  **********************************************************/
  662. void saybibi(struct Vars *v)
  663. {
  664.   while (TRUE)
  665.   {
  666.     stohdr(v, 0L);
  667.     zsbhdr(v, ZFIN);
  668.     sendbuf(v);
  669.     switch (zgethdr(v))
  670.     {
  671.     case ZFIN:
  672.       sendline(v, 'O');
  673.       sendline(v, 'O');
  674.       sendbuf(v);
  675.       /* fallthrough... */
  676.     case ZCAN:
  677.     case RCDO:
  678.     case TIMEOUT:
  679.       return;
  680.     }
  681.   }
  682. }                /* End of void saybibi() */
  683. /* End of Send.c source */
  684.